package com.example.demo.api;

import com.example.demo.game.*;
import com.example.demo.model.User;
import com.example.demo.service.impl.UserServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.util.Hashtable;
import java.util.Random;


@Component
@Slf4j
public class GameAPI extends TextWebSocketHandler {
    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private RoomManager roomManager;

    @Autowired
    private OnLineUserManager onLineUserManager;

    @Autowired
       private   UserServiceImpl userService;
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {

        User user1 = (User) session.getAttributes().get("userInfo");
        System.out.println("当前用户为xx："+user1);
        log.info("session为：”"+session);
        GameReadyResponse gameReadyResponse = new GameReadyResponse();
      log.info("进入游戏房间的连接");
      onLineUserManager.TiaoShi();
        User user  = (User) session.getAttributes().get("userInfo");
        if(user==null||user.getUserId()<=0){      //如果没有登录就直接访问这个游戏房间的页面就会抛
            gameReadyResponse.setOk(false);
            gameReadyResponse.setReason("用户未登录");
            String json = objectMapper.writeValueAsString(gameReadyResponse);
            session.sendMessage(new TextMessage(json));
            return;
        }

        //判定是不是多开(排除同一个用户一起对战，各种排除
        if(roomManager.getUserSessionFromGameRoom(user.getUserId())!=null){
            gameReadyResponse.setOk(true);                        //这里汤老师设置的为false;;我这里设置为true是没有毛病的
            log.info("这里出大问题了");
            gameReadyResponse.setReason("禁止多开游戏页面");
            gameReadyResponse.setMessage("repeatConnection");
            String json = objectMapper.writeValueAsString(gameReadyResponse);
            session.sendMessage(new TextMessage(json));
            session.close();    //这里我自己加了这个，汤老师没加
            return;
        }
        //当用户登录正常的时候；那我们就需要判断该用户在不在游戏房间里(游戏房间我们也通过concurrentHashMap存储起来了）

       roomManager.UserEnterGameRoom(user1.getUserId(), session);
        //设置玩家进入对战房间（上网的青年，room就是我们要玩的电脑，我们就需要先登录机子；用户1
    Room room = roomManager.getRoomByUserId(user.getUserId());
        System.out.println("来到下面");
        Random random = new Random();
      int tmp =  random.nextInt(10);   //这里我们设置先手（谁先出，我们规定如果tmp为偶数的话就让先进入房间的出，否则让后进入的先出）
       synchronized (room) {
           if (room.getUser1() == null) {            //这里就会有线程安全问题，并发的情况下，两个客户端如果同时处理到这里的逻辑(都认为自己是先手方），那么此时user1为空，所以
                                                       //两个玩家同时赋值给了玩家1；这样玩家2就没有人了；这样就出问题了,我们给房间进行加锁
               System.out.println("当前用户谁先获取到锁了"+user1);                                      //这里加的锁对象是实例对象，而不是类对象，因为游戏房间有很多，一个房间有一个房间的并发问题，
                                                      //但是两个房间之间不会有线程安全问题，因为我们用户设定的前提是先得找到对应游戏房间
               room.setUser1(user);
               room.setWhiteUser(user.getUserId());
               return;    //这里如果不return的话，可能两个玩家是同一个人;;return之后玩家1就等玩家2了（不是return了，为啥是等了；注意这里是return没有给
                            //给前端返回数据，要返回得用session.sendMessage;所以这里就看作为等待了
           }
           if (room.getUser2() == null) {

               //进入这个逻辑就代表玩家2也准备就绪了
               room.setUser2(user);
               if (tmp % 2 == 0) {              //这里都准备就绪再判断进行设置，要不然可能会发生错误
                   room.setWhiteUser(room.getUser1().getUserId());   //为偶数让先进入的先出
               } else {
                   room.setWhiteUser(user.getUserId());     //这里的user是后出的所以不需要通过room再去获取了
               }
               //通知玩家1
               noticeGameReady(room, room.getUser1(), room.getUser2());

               //通知玩家2
               noticeGameReady(room, room.getUser2(), room.getUser1());
               return;
           }
       }

        //程序正常不可能到这里，如果到这里就代表我们玩家满了，然后第三个玩家想要来这个房间玩，这里我们就需要提示第三个玩家房间满了

        gameReadyResponse.setOk(false);
        gameReadyResponse.setReason("房间已经满了");
        String json = objectMapper.writeValueAsString(gameReadyResponse);
        session.sendMessage(new TextMessage(json));

    }

    private void noticeGameReady(Room room, User thisUser, User thatUser) throws IOException {

        GameReadyResponse response = new GameReadyResponse();
        response.setOk(true);
        response.setThisUserId(thisUser.getUserId());
        response.setThatUserId(thatUser.getUserId());
        response.setRoomId(room.getRoomId());
        response.setMessage("gameReady");
        response.setReason("");
        response.setWhiteUser(room.getWhiteUser());
      WebSocketSession session = roomManager.getUserSessionFromGameRoom(thisUser.getUserId()) ;
      log.info("这里session为:"+session);
        String json = objectMapper.writeValueAsString(response);
        session.sendMessage(new TextMessage(json));

    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {

        GameRequest gameRequest = new GameRequest();
        User user = (User) session.getAttributes().get("userInfo");
        if(user==null||user.getUserId()<=0){

            log.info("当前用户尚未登录");
            return;
        }

        Room room = roomManager.getRoomByUserId(user.getUserId());
      /*  log.info(room.toString());*/
        String payload = message.getPayload();
        gameRequest = objectMapper.readValue(payload,GameRequest.class);
        room.putChess(gameRequest,room);

    }



    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        //连接关闭后，我们就需要让玩家下线
        log.info("游戏房间出现异常-连接进入关闭");
        User user = (User) session.getAttributes().get("userInfo");
        if(user==null||user.getUserId()<=0){   //如果设置登录拦截器就不会出现这里的问题了
            //用户未登录

            MatchResponse matchResponse = new MatchResponse();
            matchResponse.setOk(false);
            matchResponse.setReason("用户未登录");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(matchResponse)));
            return;
        }
       /* WebSocketSession socketSession = onLineUserManager.getUserSessionFromGameRoom(user.getUserId());*/
        WebSocketSession socketSession =roomManager.getUserSessionFromGameRoom(user.getUserId());
        if(socketSession==session){
            roomManager.UserRemoveGameRoom(user.getUserId());
        }


    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        //连接关闭后，我们就需要让玩家下线
        log.info("连接进入关闭");
        User user = (User) session.getAttributes().get("userInfo");
        if(user==null||user.getUserId()<=0){   //如果设置登录拦截器就不会出现这里的问题了
            //用户未登录

            MatchResponse matchResponse = new MatchResponse();
            matchResponse.setOk(false);
            matchResponse.setReason("用户未登录");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(matchResponse)));
            return;
        }

        log.info("从此处开始进行调试");
        log.info(user.toString());

        WebSocketSession socketSession =roomManager.getUserSessionFromGameRoom(user.getUserId());

       Room room = roomManager.getRoomByUserId(user.getUserId());
       if(room==null){
           log.info("这里直接关闭");//这里如果不加得话，游戏正常得到输赢后（在Room类中，那里就会把游戏房间删除，所以这里room就为空了，所以我们就得判定)，点击进入大厅就会关闭socket连接，会调用这个连接关闭方法，
             roomManager.UserRemoveGameRoom(user.getUserId());
             return ;//如果你把这个room去了，你得到正常输赢当返回到大厅就会提示多开因为当游戏结束后，
       }             //这里必须return,这里return肯定首先是因为我们有人离开房间了，所以这个游戏房间就销毁了；
       log.info("打印room:"+room.getUser1().getUserId());
       log.info("打印room:"+room.getUser2().getUserId());
       if(room.getUser1().getUserId()==user.getUserId()){    //这里解决的问题是当两个玩家玩的好好的，突然有一个玩家掉线了，需要告知没有断开连接玩家输赢情况
           log.info("玩家2没掉线呢：");
           GameResponse gameResponse = new GameResponse();
           gameResponse.setUserId(room.getUser2().getUserId());
           gameResponse.setWinner(room.getUser2().getUserId());
           gameResponse.setMessage("putChess");     //这里的响应会返回给前端的第164行，所以你必须设置message为putChess(根据前端）
           String json = objectMapper.writeValueAsString(gameResponse);
           log.info(json);
        /*   WebSocketSession session1 = onLineUserManager.getUserSessionFromGameRoom(room.getUser2().getUserId());*/
           WebSocketSession session1 =roomManager.getUserSessionFromGameRoom(room.getUser2().getUserId());
           log.info("这里的session1为："+session1);

           userService.updateWinnerUserById(gameResponse.getWinner());
           userService.updateLoserUserById(room.getUser1().getUserId());
           session1.sendMessage(new TextMessage(json));             //这里当前用户1关闭了，但是用户2有还有连接，我们需要把通知告知有连接的用户2，这里以前我就弄反了
                                                                   //当初是发给断开连接的一方了，所以就错了，断开连接的一方看不到，而且由于在close方法了，不能给断开连接的玩家发消息了
           roomManager.remove(room.getUser1().getUserId(),room.getUser2().getUserId());  //这里需要移除游戏房间，必须得，要不然回到游戏大
       }
        if(room.getUser2().getUserId()==user.getUserId()){
            log.info("玩家1没掉线呢");
            GameResponse gameResponse = new GameResponse();
            gameResponse.setUserId(room.getUser1().getUserId());
            gameResponse.setWinner(room.getUser1().getUserId());
            gameResponse.setMessage("putChess");
            String json = objectMapper.writeValueAsString(gameResponse);
            log.info(json);
        /*    WebSocketSession session1 = onLineUserManager.getUserSessionFromGameRoom(room.getUser1().getUserId());*/
            WebSocketSession session1 =roomManager.getUserSessionFromGameRoom(room.getUser1().getUserId());
            log.info("这里的session1为"+session1);
            userService.updateWinnerUserById(gameResponse.getWinner());
            userService.updateLoserUserById(room.getUser2().getUserId());
           session1.sendMessage(new TextMessage(json));
            roomManager.remove(room.getUser1().getUserId(),room.getUser2().getUserId());
        }

        if(socketSession==session){      //。。。
            roomManager.UserRemoveGameRoom(user.getUserId());
        }


    }
}
